#include "chatservice.hpp"
#include "public.hpp"

#include <muduo/base/Logging.h>
#include <string>
#include <vector>
#include <map>

// 获取单例对象的接口函数
ChatService *ChatService::getInstance()
{
    // 在静态函数里创建静态对象并返回指针
    static ChatService service;
    return &service;
}

// 注册消息以及对应的Handler回调操作
ChatService::ChatService()
{
    // 用户基本业务管理相关事件处理回调注册
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});
    _msgHandlerMap.insert({LOGOUT_MSG, std::bind(&ChatService::logout, this, _1, _2, _3)});
    _msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});
    _msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFriend, this, _1, _2, _3)});

    // 群组业务管理相关事件处理回调注册
    _msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1, _2, _3)});

    // 连接 redis 服务器
    if(_redis.connect())
    {
        // 设置上报消息的回调
        _redis.init_notify_handler(std::bind(&ChatService::handleRedisSubscribeMessage, this, _1, _2));
    }
}

// 处理登录业务 id pwd pwd
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int id = js["id"];
    string pwd = js["password"];

    User user = _userModel.query(id);

    if (user.getId() == id && user.getPassword() == pwd)
    {
        if (user.getState() == "online")
        {
            // 用户已经登录，不允许重复登陆
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "this user already login, login another.";
            conn->send(response.dump()); // 返回客户端
        }
        else
        {
            // 登录成功
            {
                // 记录用户连接信息
                std::lock_guard<std::mutex> lock(_connMutex);
                _userConnMap.insert({user.getId(), conn});
            }

            // id 用户登录成功后向 redis 订阅 channel(id)
            _redis.subscribe(id);  // id 就是用户 id

            // 更新用户状态信息
            user.setState("online");
            _userModel.updateState(user);

            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 0;
            response["id"] = user.getId();
            response["name"] = user.getName();

            // 查询该用户是否有离线消息
            std::vector<std::string> offlineMsgVec = _offlineMsgModel.query(id);
            if (!offlineMsgVec.empty())
            {
                response["offlinemsg"] = offlineMsgVec;
                // 读取该用户的离线消息后，把该用户的所有离线消息删除
                _offlineMsgModel.remove(id);
            }

            // 查询该用户的好友信息并返回
            std::vector<User> userVec = _friendModel.query(id);
            if (!userVec.empty())
            {
                std::vector<std::string> vec2;
                for (User &user : userVec)
                {
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    vec2.push_back(js.dump());
                }
                response["friends"] = vec2;
            }

            // 查询该用户所在的群组信息
            std::vector<Group> groupVec = _groupModel.queryGroups(id);
            if(!groupVec.empty())
            {
                // group:[{groupid:[xxx, xxx, xxx, xxx]}]
                std::vector<std::string> vec2;
                for(Group &group : groupVec)
                {
                    json groupJs;
                    groupJs["id"] = group.getId();
                    groupJs["groupname"] = group.getName();
                    groupJs["groupdesc"] = group.getDesc();

                    std::vector<GroupUser> groupFriendVec = group.getUsers();
                    std::vector<std::string> vec3;
                    if(!groupFriendVec.empty())
                    {
                        for (GroupUser &groupFriend : groupFriendVec)
                        {
                            json groupFriendJs;
                            groupFriendJs["id"] = groupFriend.getId();
                            groupFriendJs["name"] = groupFriend.getName();
                            groupFriendJs["state"] = groupFriend.getState();
                            groupFriendJs["role"] = groupFriend.getRole();
                            vec3.push_back(groupFriendJs.dump());
                        }
                    }
                    groupJs["users"] = vec3;

                    vec2.push_back(groupJs.dump());
                }
                response["groups"] = vec2;
            }

            conn->send(response.dump()); // 返回客户端
        }
    }
    else
    {
        // 用户不存在，或用户存在但密码错误，登录失败
        if (user.getId() == -1)
        {
            // 用户不存在
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 1;
            response["errmsg"] = "userid is invalid!";
            conn->send(response.dump()); // 返回客户端
        }
        else // user.getId() != id && user.getPassword() != pwd
        {
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 1;
            response["errmsg"] = "userid or password is wrong!";
            conn->send(response.dump()); // 返回客户端
        }
    }
}

// 处理注册业务  填写 name password
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    string name = js["name"];
    string pwd = js["password"];

    User user;
    user.setName(name);
    user.setPassword(pwd);
    bool state = _userModel.insert(user);

    if (state)
    {
        // 注册成功
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 0;
        response["id"] = user.getId();

        conn->send(response.dump()); // 返回客户端
    }
    else
    {
        // 注册失败
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 1;

        conn->send(response.dump()); // 返回客户端
    }
}

// 一对一聊天业务
void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int toid = js["to"].get<int>(); // 对端id

    {
        std::lock_guard<std::mutex> lock(_connMutex);
        auto it = _userConnMap.find(toid);
        if (it != _userConnMap.end())
        {
            // toid在线，转发消息，服务器主动推送消息给 toid 用户
            it->second->send(js.dump());
            return;
        }
        // 消息处理也不能放在外边并发，不然可能处理到一半连接没了
    }

    // 查询 toid 是否在线，在其它服务器上
    User user = _userModel.query(toid);
    if(user.getState() == "online")
    {
        _redis.publish(toid, js.dump());
        return ;
    }

    // toid不在线，存储离线消息
    _offlineMsgModel.insert(toid, js.dump());
}

// 添加好友业务 msgid di friendid
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int friendid = js["friendid"].get<int>();

    // 存储好友信息
    _friendModel.insert(userid, friendid);
}

// 创建群组业务
void ChatService::createGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    std::string groupname = js["groupname"];
    std::string groupdesc = js["groupdesc"];

    // 存储新创建的群组信息
    Group group(-1, groupname, groupdesc);
    if(_groupModel.createGroup(group))
    {
        // 存储群组创建人信息
        _groupModel.addGroup(userid, group.getId(), "creator");
    }
}

// 加入群组业务
void ChatService::addGroup(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    _groupModel.addGroup(userid, groupid, "normal");
}

// 群组聊天业务
void ChatService::groupChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    std::vector<int> useridVec = _groupModel.queryGroupUsers(userid, groupid);  // 获取此群其它用户id

    std::lock_guard<std::mutex> lock(_connMutex);
    for(int id: useridVec)
    {
        auto it = _userConnMap.find(id);  // 该用户是否在线
        if(it != _userConnMap.end())
        {
            // 在线，转发
            it->second->send(js.dump());
        }
        else
        {
            // toid 是否在线
            User user = _userModel.query(id);
            if(user.getState() == "online")
            {
                _redis.publish(id, js.dump());
            }
            else
            {
                // 不在线，离线存储
                _offlineMsgModel.insert(id, js.dump());
            }
        }
    }
}

// 获取消息对应的处理器
MsgHander ChatService::getHander(int msgid)
{
    // 记录错误日志，msgid没有对应的事件处理回调
    auto it = _msgHandlerMap.find(msgid);
    if (it == _msgHandlerMap.end())
    {
        // 返回一个默认的处理器，空操作
        return [=](const TcpConnectionPtr &conn, json &js, Timestamp time)
        {
            LOG_ERROR << "msgid:" << msgid << "can not find handler!"; // muduo 的日志已经包含换行了
        };
    }
    else
    {
        return _msgHandlerMap[msgid];
    }
}

// 处理注销业务
void ChatService::logout(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int id = js["id"].get<int>();

    {
        std::lock_guard<std::mutex> lock(_connMutex);
        auto it = _userConnMap.find(id);
        if(it != _userConnMap.end())
        {
            _userConnMap.erase(it);

        }
    }

    // 用户注销，相当于下线，取消订阅
    _redis.unsubscribe(id);

    // 更新用户的状态信息
    User user(id, "", "", "offline");
    _userModel.updateState(user);
}

// 处理客户端异常退出
void ChatService::clientCloseException(const TcpConnectionPtr &conn)
{
    User user;
    {
        std::lock_guard<std::mutex> lock(_connMutex);
        for (auto it = _userConnMap.begin(); it != _userConnMap.end(); it++)
        {
            if (it->second == conn)
            {
                // 之前登录过了
                // 从map表删除用户的连接信息
                user.setId(it->first);
                _userConnMap.erase(it);
                break;
            }
        }
    }

    // 用户注销，相当于下线，取消订阅
    _redis.unsubscribe(user.getId());

    // 更新用户的状态信息
    if (user.getId() != -1)
    {
        user.setState("offline");
        _userModel.updateState(user);
    }
}

// 服务器异常，业务重置方法
void ChatService::reset()
{
    // 把 online 状态的用户设置成 offline
    _userModel.resetState();
}

// 从 redis 消息队列中获取订阅的消息
void ChatService::handleRedisSubscribeMessage(int userid, std::string msg)
{
    // 完成转发操作
    std::lock_guard<std::mutex> lock(_connMutex);
    auto it = _userConnMap.find(userid);
    if(it != _userConnMap.end())
    {
        it->second->send(msg);
        return ;
    }

    // 存储该用户的离线消息（可能转发的过程中对端下线了）
    _offlineMsgModel.insert(userid, msg);
}